﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Caching;
using System.Web.Mvc;
using System.Web.Security;
using System.Xml;
using Amib.Threading;
using HtmlAgilityPack;
using Kaio.Core;
using log4net;
using Kaio.Core.Extension;

namespace MvcForum.Net.Bussiness
{
    public static class ForumContext
    {

        static readonly ILog Log = LogManager.GetLogger(typeof(ForumContext));

        public static string Version
        {
            get
            {
                return Assembly.GetExecutingAssembly().GetName().Version.ToString();
            }
        }



        public static HttpContext Context
        {
            get
            {

                return HttpContext.Current;
            }

        }


        public static string OutputCacheKey
        {
            get
            {
                if (HttpContext.Current.Session["OutputCacheKey"] == null)
                {
                    Context.Session["OutputCacheKey"] = Guid.NewGuid().ToString();
                }
                return Context.Session["OutputCacheKey"].ToString();
            }
        }

        #region SmartThreadPool
        // Create the SmartThreadPool instance

        private static readonly Object _syncRootThread = new Object();
        private static volatile SmartThreadPool _smartThreadPool;

        public static SmartThreadPool ThreadPool
        {
            get
            {

                if (_smartThreadPool == null)
                {
                    lock (_syncRootThread)
                    {
                        if (_smartThreadPool == null)
                        {
                            var stpStartInfo = new STPStartInfo
                            {
                                /*StartSuspended = true,*/
                                DisposeOfStateObjects = true,
                                /*  UseCallerCallContext = true,*/
                                /* UseCallerHttpContext = true,            */
                                MinWorkerThreads = 0,
                                MaxWorkerThreads = 10,
                                ThreadPriority = ThreadPriority.Normal
                            };
                            _smartThreadPool = new SmartThreadPool(stpStartInfo);

                        }

                    }
                    //_smartThreadPool.Start();
                }


                /* if(_smartThreadPool.IsShuttingdown)
                 {
                     _smartThreadPool.Start();    
                 }*/

                try
                {
                    _smartThreadPool.Start();

                }
                finally
                {

                }

                return _smartThreadPool;

            }
        }


        #endregion


        // Fix Share Host Godaddy
        public static string ApplicationPath
        {
            get
            {
                var p = ConfigurationManager.AppSettings["AppPath"];
                return !string.IsNullOrWhiteSpace(p) ? p : Context.Request.ApplicationPath;
            }
        }




        public static ForumConfiguration Configuration
        {
            get { return ForumConfiguration.Instant; }
        }

        #region Localizer

        public static Dictionary<string, NameValueCollection> Localizer
        {
            get
            {

                string _cacheKey = string.Format(CacheKeys.LOCALIZER, Configuration.Language);


                var objectValues = (Dictionary<string, NameValueCollection>)CacheHelper.Get(_cacheKey);

                if (objectValues == null)
                {
                    string _filePath = Context.Server.MapPath(string.Format("{0}languages/{1}.xml", ApplicationPath, Configuration.Language));

                    if (!File.Exists(_filePath))
                        throw (new ApplicationException("Invalid language file " + _filePath));

                    var _doc = new XmlDocument();
                    _doc.Load(_filePath);

                    objectValues = new Dictionary<string, NameValueCollection>();


                    foreach (XmlNode node in _doc.DocumentElement.ChildNodes)
                    {
                        var values = new NameValueCollection();

                        var childNodes = node.ChildNodes;

                        if (childNodes.Count > 0)
                        {
                            foreach (XmlNode childNode in childNodes)
                            {
                                values.Add(childNode.Name, childNode.InnerText);
                            }
                        }

                        if (!objectValues.ContainsKey(node.Name))
                            objectValues.Add(node.Name.ToLower(), values);

                    }


                    /*if(objectValues.Count==0)
                        throw (new InvalidDataException(string.Format( "Load language file {0} data error.",_filePath)));*/


                    CacheHelper.Add(_cacheKey, objectValues, new CacheDependency(_filePath));

                }

                return objectValues;

            }
        }


        private static NameValueCollection GetPage(string pageName)
        {
            try
            {
                return Localizer[pageName.ToLower()];
            }
            catch (Exception)
            {

                throw new ArgumentOutOfRangeException(string.Format("The key: {0} not exists.", pageName));
            }
        }


        public static string GetText(string page, string entry)
        {
            var vals = GetPage(page);

            try
            {
                return (vals != null) ? vals[entry] : String.Empty;
            }
            catch (Exception)
            {

                throw new ArgumentOutOfRangeException(string.Format("The key: {0} not exists.", entry));
            }

        }

        public static string GetText(string page, Messages entry)
        {
            return GetText(page, entry.ToString());
        }

        public static MvcHtmlString GetMvcText(string page, string entry)
        {
            return new MvcHtmlString(GetText(page, entry));
        }

        #endregion


        #region NavMenu Render

        public static List<ForumUrl> GetNavMenu()
        {




            string _filePath = Context.Server.MapPath(string.Format("{0}languages/{1}.navmenu.xml", ApplicationPath, Configuration.Language));

            if (!File.Exists(_filePath))
                throw (new ApplicationException("Invalid language file " + _filePath));

            var _doc = new XmlDocument();
            _doc.Load(_filePath);


            var _urls = new List<ForumUrl>();

            var _nodes = _doc.DocumentElement.ChildNodes;

            foreach (XmlNode _node in _nodes)
            {

                bool _allowShow;



                if (!Logined)
                {

                    _allowShow = _node.Attributes == null || _node.Attributes["showLogined"] == null || !bool.Parse(_node.Attributes["showLogined"].Value);

                }
                else
                {
                    _allowShow = _node.Attributes != null && (_node.Attributes["showLogined"] == null || bool.Parse(_node.Attributes["showLogined"].Value));

                }


                if (_allowShow)
                {

                    string _class = _node.Attributes != null && _node.Attributes["class"] != null
                                        ? _node.Attributes["class"].Value
                                        : string.Empty;
                    string _link = _node.Attributes != null ? _node.Attributes["link"].Value : string.Empty;

                    var _url = GetAbsoluteUrl(_link);

                    _urls.Add(new ForumUrl
                                  {
                                      Title = _node.InnerText,
                                      Url = _url,
                                      ShortUrl = _url,
                                      CssClass = "bold " + _class
                                  }
                        );

                }

            }

            return _urls;



        }
        #endregion


        #region IPLock


        public static bool IpAddressIsBanned()
        {
            var _IPs = IPLockRepo.List();
            if (_IPs != null && _IPs.Count > 0)
            {
                return _IPs.Any(x => IPLocked(x.Mask));
            }
            return false;
        }


        public static bool IPLocked(string ipMask)
        {

            var _clientIp = ClientIp;

            if (ipMask.IndexOf("*") >= 0)
            {
                var _s = ipMask.Split('.');

                for (int i = 0; i < _s.Length; i++)
                {
                    if (_s[i].Equals("*"))
                    {
                        for (int x = 0; x < 255; x++)
                        {
                            _s[i] = x.ToString();

                            string _ip = String.Join(".", _s);

                            if (_ip.Equals(_clientIp))
                            {
                                return true;
                            }

                        }
                    }
                }
            }

            return ipMask.Equals(_clientIp);
        }

        #endregion


        #region Html Sanitizer

        public static HtmlSanitizer HtmlSanitizer
        {
            get
            {
                var _htmlSanitizer = CacheHelper.Get<HtmlSanitizer>(CacheKeys.HTML_SANITIZER);

                if (_htmlSanitizer == null)
                {
                    string _filePath = Context.Server.MapPath(string.Format("{0}html.config", ApplicationPath));

                    if (!File.Exists(_filePath))
                        throw (new ApplicationException("Invalid language file " + _filePath));

                    var _doc = new XmlDocument();
                    _doc.Load(_filePath);

                    var _nodes = _doc.DocumentElement.ChildNodes;

                    var _whiteListTags = new Dictionary<string, List<string>>();


                    foreach (XmlNode _node in _nodes)
                    {

                        List<string> _attrs = null;

                        var childNodes = _node.ChildNodes;

                        if (childNodes.Count > 0)
                        {

                            _attrs = (from XmlNode childNode in childNodes select childNode.Name).ToList();
                        }

                        _whiteListTags.Add(_node.Name, _attrs);
                    }

                    _htmlSanitizer = new HtmlSanitizer(_whiteListTags);


                }

                return _htmlSanitizer;

            }
        }

        #endregion

        #region User and Session

        public static string ClientIp
        {
            get
            {
                string _ip = Context.Request.UserHostAddress;
                if (_ip == "::1" || string.IsNullOrEmpty(_ip)) _ip = "127.0.0.1";

                return _ip;
            }
        }

        public static string SessionID
        {
            get
            {
                if (Context.Session[CacheKeys.SESSIONID] == null)
                {
                    Context.Session[CacheKeys.SESSIONID] = Guid.NewGuid().ToString();
                }
                return Context.Session[CacheKeys.SESSIONID].ToString();


            }
        }

        public static bool Logined
        {
            get
            {
                return UserInfo != null;
            }
        }

        public static bool IsLogined
        {
            get { return Logined; }
        }


        public static UserInfo UserInfo
        {
            get
            {


                var _u = (UserInfo)Context.Session[CacheKeys.USER_INFO];
                if (_u == null)
                {
                    try
                    {
                        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];

                        if (authCookie != null)
                        {
                            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                            var id = new FormsIdentity(authTicket);
                            var principal = new GenericPrincipal(id, null);
                            Context.User = principal;

                            string[] _userData = authTicket.UserData.Split(';');

                            _u = UsersRepo.LogIn(_userData[0], _userData[1], false);

                            Context.Session[CacheKeys.USER_INFO] = _u;

                        }

                    }
                    catch (Exception)
                    {
                        _u = null;
                    }


                }

                return _u;

            }
        }



        public static void SetAuthenticationCookie(string username, string password, bool isPersit)
        {



            //FormsAuthentication.SetAuthCookie(username,isPersit);

            /*var Authticket = new
                           FormsAuthenticationTicket(1,
                           username,
                           DateTime.Now,
                           DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
                           true,
                           string.Format("{0};{1}", username, password),
                           FormsAuthentication.FormsCookiePath);*/

            /*string hash = Encrypt(username,password);


            var Authcookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash)
            {
                HttpOnly = FormsAuthentication.RequireSSL,
                Path = FormsAuthentication.FormsCookiePath,
                Domain = FormsAuthentication.CookieDomain,
                Expires = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes)
            };*/

            var Authticket = new
                          FormsAuthenticationTicket(1,
                          username,
                          DateTime.Now,
                          DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
                          true,
                          string.Format("{0};{1}", username, password),
                          FormsAuthentication.FormsCookiePath);

            string hash = FormsAuthentication.Encrypt(Authticket);

            var Authcookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash)
            {
                HttpOnly = true,
                Path = FormsAuthentication.FormsCookiePath,
                Domain = FormsAuthentication.CookieDomain
            };

            if (Authticket.IsPersistent) Authcookie.Expires = Authticket.Expiration;

            Context.Response.AppendCookie(Authcookie);

        }

        public static void SignOut()
        {
            FormsAuthentication.SignOut();

            HttpCookie cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
            if (cookie != null)
            {
                cookie.Expires = DateTime.UtcNow.AddYears(-1);
                Context.Response.AppendCookie(cookie);
            }

            Context.Session.Abandon();

        }


        public static Moderator Mod(int fid)
        {
            return Logined ? UserInfo.Mod(fid) : null;
        }


        public static bool IsModerated(int groupId)
        {
            return Logined && UserInfo.IsModerated(groupId);
        }

        public static bool IsAdmin
        {
            get
            {
                return Logined && UserInfo.IsAdmin;
            }
        }

        public static bool IsSuperMod
        {
            get { return Logined && (UserInfo.IsAdmin || UserInfo.IsSuperModerator); }
        }




        public static bool ForumAllowRoles(string allowRoles)
        {

            if (IsSuperMod) return true;

            if (!string.IsNullOrWhiteSpace(allowRoles))
            {
                if (Logined)
                {
                    string[] _roles = allowRoles.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);

                    return _roles.Any(role => role == UserInfo.RoleId.ToString());

                }
                return false;
            }

            return true;

        }


        /*

                public static bool AllowEdit(Topic t)
                {

                    if (Logined)
                    {
                        var _mod = UserInfo.Mod(t.ForumId);
                        bool _isTopic = t.ReplyTo == 0;
                        bool _isPost = !_isTopic;
                        bool _own = t.UserId == UserInfo.UserId;

                        return IsSuperMod ||
                               (_own && ((_isTopic && (!t.IsLocked && UserInfo.Permissions.EditOwnTopics) || (t.IsLocked && UserInfo.Permissions.EditLockedTopics)) || (_isPost && UserInfo.Permissions.EditOwnPosts))) ||
                               (_mod != null && ((_isTopic && _mod.EditTopics) || (_isPost && _mod.EditPosts)));

                        //(t.ReplyTo == 0 && (t.UserId == UserInfo.UserId && UserInfo.Permissions.EditOwnTopics ) || (_mod != null && _mod.EditTopics)) || (t.ReplyTo > 0 && (t.UserId == UserInfo.UserId && UserInfo.Permissions.EditOwnPosts) || (_mod != null && _mod.EditPosts));
                    }

                    return false;

                }

                public static bool AllowDel(Topic t)
                {

                    if (Logined)
                    {
                        var _mod = UserInfo.Mod(t.ForumId);
                        bool _isTopic = t.ReplyTo == 0;
                        bool _isPost = !_isTopic;
                        bool _own = t.UserId == UserInfo.UserId;

                        return IsSuperMod ||
                               (_own && ((_isTopic && (!t.IsLocked && UserInfo.Permissions.DeleteOwnTopics) || (t.IsLocked && UserInfo.Permissions.DeleteLockedPosts)) || (_isPost && UserInfo.Permissions.DeleteOwnPosts))) ||
                               (_mod != null && ((_isTopic && _mod.DeleteTopics) || (_isPost && _mod.DeletePosts)));


                        //return IsSuperMod || (t.ReplyTo == 0 && (t.UserId == UserInfo.UserId && UserInfo.Permissions.DeleteOwnTopics) || (_mod != null && _mod.DeleteTopics)) || (t.ReplyTo > 0 && (t.UserId == UserInfo.UserId && UserInfo.Permissions.DeleteOwnPosts) || (_mod != null && _mod.DeletePosts));

                    }
                    return false;
                }
        */



        public static List<SelectListItem> ModeratorOptions(int fid)
        {
            var _widthSelected = GetText(LanguageSection.MODERATOR_TOOL, "WITH_SELECTED");


            var _actionTypes = new List<SelectListItem>();


            if (IsSuperMod)
            {
                var _moderateOptions = Enum.GetValues(typeof(ModerateCommandType));

                _actionTypes = (from object value in _moderateOptions
                                select new SelectListItem
                                {
                                    Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), value))),
                                    Value = ((int)value).ToString()
                                }).ToList();

            }
            else
            {
                var _mod = Mod(fid);

                if (_mod != null)
                {
                    // Approved
                    if (_mod.ApprovePosts || _mod.ApproveTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Approve))),
                            Value = ((int)ModerateCommandType.Approve).ToString()

                        });
                    // Queue
                    if (_mod.QueuePosts || _mod.QueueTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Queue))),
                            Value = ((int)ModerateCommandType.Queue).ToString()

                        });

                    // Pin
                    if (_mod.UnLockTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Pin))),
                            Value = ((int)ModerateCommandType.Pin).ToString()

                        });

                    // Pin
                    if (_mod.UnPinTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.UnPin))),
                            Value = ((int)ModerateCommandType.UnPin).ToString()

                        });



                    // Move
                    if (_mod.MoveTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Move))),
                            Value = ((int)ModerateCommandType.Move).ToString()

                        });

                    // UnMove
                    if (_mod.UnMoveTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.UnMove))),
                            Value = ((int)ModerateCommandType.UnMove).ToString()

                        });



                    // Lock
                    if (_mod.LockTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Lock))),
                            Value = ((int)ModerateCommandType.Lock).ToString()

                        });

                    // UnLock
                    if (_mod.UnLockTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.UnLock))),
                            Value = ((int)ModerateCommandType.UnLock).ToString()

                        });

                    // Delete
                    if (_mod.DeletePosts || _mod.DeleteTopics)
                        _actionTypes.Add(new SelectListItem
                        {
                            Text = string.Format("{0} {1}", _widthSelected, GetText(LanguageSection.MODERATOR_TOOL, Enum.GetName(typeof(ModerateCommandType), ModerateCommandType.Del))),
                            Value = ((int)ModerateCommandType.Del).ToString()

                        });


                }

            }
            return _actionTypes;

        }


        public static bool AllowAccess(ModerateCommandType cmd, int fid)
        {
            if (IsSuperMod) return true;

            Moderator _mod = Mod(fid);


            return _mod != null && (cmd == ModerateCommandType.Approve && (_mod.ApprovePosts || _mod.ApproveTopics) ||
                                    cmd == ModerateCommandType.Del && (_mod.DeletePosts || _mod.DeleteTopics) ||
                                    cmd == ModerateCommandType.Queue && (_mod.QueuePosts || _mod.QueueTopics) ||
                                    cmd == ModerateCommandType.Lock && _mod.LockTopics ||
                                    cmd == ModerateCommandType.UnLock && _mod.UnLockTopics ||
                                    cmd == ModerateCommandType.Move && _mod.MoveTopics ||
                                    cmd == ModerateCommandType.UnMove && _mod.UnMoveTopics ||
                                    cmd == ModerateCommandType.Pin && _mod.PinTopics ||
                                    cmd == ModerateCommandType.UnPin && _mod.UnPinTopics
                                   );

        }

        public static bool AllowAccess(CommandType cmd, Forum f, Topic t)
        {

            if (Logined)
            {
                // break is admin
                if (IsSuperMod) return true;

                bool _flag = false, _isTopic, _own, _isPost;

                Moderator _mod;

                switch (cmd)
                {
                    case CommandType.NewPost:
                        _flag = f != null && f.AllowPostWithIn && ForumAllowRoles(f.AllowRoles) &&
                                (!f.IsLocked && UserInfo.Permissions.PostNewTopics ||
                                 f.IsLocked && UserInfo.Permissions.PostWithinLockedForums);
                        break;

                    case CommandType.NewPoll:
                        _flag = f != null && f.AllowPostWithIn && ForumAllowRoles(f.AllowRoles) &&
                                (!f.IsLocked && UserInfo.Permissions.PostNewPolls ||
                                 f.IsLocked && UserInfo.Permissions.PostWithinLockedForums);
                        break;
                    case CommandType.Reply:
                        _flag = t != null && ((!t.IsLocked && (t.IsPoll && UserInfo.Permissions.PostReplyToPolls) || UserInfo.Permissions.PostReplyToTopics) ||
                                 (t.IsLocked && UserInfo.Permissions.PostWithinLockedTopics));
                        break;

                    case CommandType.Edit:
                        _mod = UserInfo.Mod(t.ForumId);
                        _isTopic = t.ReplyTo == 0;
                        _isPost = !_isTopic;
                        _own = t.UserId == UserInfo.UserId;
                        _flag = (_own && ((_isTopic && (!t.IsLocked && UserInfo.Permissions.EditOwnTopics) || (t.IsLocked && UserInfo.Permissions.EditLockedTopics)) || (_isPost && UserInfo.Permissions.EditOwnPosts))) ||
                                (_mod != null && ((_isTopic && _mod.EditTopics) || (_isPost && _mod.EditPosts)));
                        break;
                    case CommandType.Delete:
                        _mod = UserInfo.Mod(t.ForumId);
                        _isTopic = t.ReplyTo == 0;
                        _isPost = !_isTopic;
                        _own = t.UserId == UserInfo.UserId;
                        _flag = (_own && ((_isTopic && (!t.IsLocked && UserInfo.Permissions.DeleteOwnTopics) || (t.IsLocked && UserInfo.Permissions.DeleteLockedPosts)) || (_isPost && UserInfo.Permissions.DeleteOwnPosts))) ||
                                (_mod != null && ((_isTopic && _mod.DeleteTopics) || (_isPost && _mod.DeletePosts)));
                        break;
                    case CommandType.LockTopic:
                        _mod = UserInfo.Mod(t.ForumId);
                        _flag = IsSuperMod || (_mod != null && _mod.LockTopics);
                        break;
                    case CommandType.UnLockTopic:
                        _mod = UserInfo.Mod(t.ForumId);
                        _flag = IsSuperMod || (_mod != null && _mod.UnLockTopics);
                        break;


                }

                return _flag;

            }

            return false;
            /*

                        return Logined && f != null && f.AllowPostWithIn &&
                            (IsSuperMod ||
                            (ForumAllowAccess(f.AllowRoles) &&
                            // Allow post new topics
                            ((cmd == CommandType.NewPost && (!f.IsLocked && UserInfo.Permissions.PostNewTopics) || (f.IsLocked && UserInfo.Permissions.PostWithinLockedForums)) ||
                            // Allow post new polls
                            (cmd == CommandType.NewPoll && (!f.IsLocked && UserInfo.Permissions.PostNewPolls) || (f.IsLocked && UserInfo.Permissions.PostWithinLockedForums)) ||
                            // Allow reply to topics && reply to polls
                            (cmd == CommandType.Reply && t != null && ((!t.IsLocked && (t.PollId > 0 && UserInfo.Permissions.PostReplyToPolls) || UserInfo.Permissions.PostReplyToTopics) || (t.IsLocked && UserInfo.Permissions.PostWithinLockedTopics)))
                            )));*/
        }


        public static bool AllowReadTopics
        {
            get { return Logined && (UserInfo.IsMod || UserInfo.Permissions.AllowReadTopics); }
        }



        public static bool AllowReadForums
        {
            get { return Logined && (UserInfo.IsMod || UserInfo.Permissions.AllowReadForums); }
        }


        public static bool AllowStickyPosts
        {
            get { return Logined && (IsSuperMod || UserInfo.Permissions.AllowStickyPosts); }
        }


        public static MvcHtmlString AvatarRender(string avatar, bool? sex)
        {
            return AvatarRender(avatar, sex, new Size(0, 0));
        }

        public static MvcHtmlString AvatarRender(string avatar, bool? sex, Size s)
        {

            if (string.IsNullOrWhiteSpace(avatar))
            {
                if (sex.HasValue)
                    avatar = sex.Value ? "male.png" : "female.png";
                else
                {
                    avatar = "unknow.png";
                }
            }

            if (s.IsEmpty)
            {
                s = new Size(Configuration.AvatarWidth, Configuration.AvatarHeight);
            }

            return new MvcHtmlString(string.Format("<div class=\"user-avatar\" style=\"width:{0}px;height:{1}px;background-image:url('{2}{3}')\"></div>", s.Width, s.Height, Configuration.AvatarPath, avatar));

        }


        public static void ClientActivity(ForumUrl url)
        {

            if (url == null || string.IsNullOrWhiteSpace(url.Url)) return;

            var _b = Context.Request.Browser;

            string _platform = _b.Platform;
            string _browser = string.Format("{0} {1}", _b.Browser, _b.Version);

            ActivityRepo.Save(SessionID, Logined ? UserInfo.UserId : 0, Logined ? UserInfo.UserName : string.Empty, ClientIp, url.Title, url.Url, url.ForumId, url.TopicId, _browser, _platform, url.IsPrivated, Configuration.PastMinutes);

        }


        #endregion


        #region DateTime Format

        private static string GetDate(string key)
        {
            return GetText(LanguageSection.DATETIME, key);
        }

        public static TimeSpan TimeZone
        {
            get
            {
                //float min = ForumConfiguration.Instance.TimeZoneOffset * 60;
                return TimeSpan.FromHours(Configuration.TimeZoneOffset);
                // return new TimeSpan(min / 60, min % 60, 0);
            }
        }

        /// <summary>
        /// Returns the user timezone offset from GMT
        /// </summary>
        public static TimeSpan TimeZoneOffsetUser
        {
            get
            {
                if (UserInfo != null)
                {


                    return TimeSpan.FromHours(UserInfo.TimeZone);
                    //return new TimeSpan(min / 60, min % 60, 0);


                }
                return TimeZone;
            }
        }


        public static DateTime ToUtcDateTime(this DateTime _dateTime)
        {
            try
            {
                return _dateTime - TimeZoneOffsetUser;
            }
            catch (Exception)
            {
                return DateTime.UtcNow - TimeZoneOffsetUser;
            }


        }

        public static DateTime ToUserTimeZoneDateTime(this DateTime _dateTime)
        {
            return _dateTime + TimeZoneOffsetUser;
        }





        public static string DateTimeFormat(DateTime _dateTime)
        {
            DateTime _dt = _dateTime + TimeZoneOffsetUser;

            return _dt.ToString(Configuration.DateTimeFormat);
        }

        public static string ShortDateTimeFormat(DateTime _dateTime)
        {
            DateTime _dt = _dateTime + TimeZoneOffsetUser;

            return _dt.ToString(GetDate("SHORT_DATE"));
        }

        public static string TimeFormat(DateTime _datetime)
        {

            _datetime += TimeZoneOffsetUser;
            try
            {
                return _datetime.ToString("hh:mm tt");
            }
            catch (Exception)
            {
                return _datetime.ToString("T");
            }
        }


        public static string PrettyDate(DateTime _date)
        {
            // 1.
            // Get time span elapsed since the date.
            TimeSpan _s = DateTime.UtcNow.Subtract(_date);

            // 2.
            // Get total number of days elapsed.
            var _dayDiff = (int)_s.TotalDays;

            // 3.
            // Get total number of seconds elapsed.
            var _secDiff = (int)_s.TotalSeconds;

            // 4.
            // Don't allow out of range values.
            if (_dayDiff < 0 || _dayDiff >= 31)
            {
                return DateTimeFormat(_date);
            }

            // 5.
            // Handle same-day times.
            if (_dayDiff == 0)
            {
                // A.
                // Less than one minute ago.
                if (_secDiff < 60)
                {
                    return string.Format(GetDate("JUST_NOW"), _secDiff == 0 ? 1 : _secDiff); //"just now";
                }
                // B.
                // Less than 2 minutes ago.
                if (_secDiff < 120)
                {
                    return string.Format(GetDate("MINUTES_AGO"), 1);// "1 minute ago";
                }
                // C.
                // Less than one hour ago.
                if (_secDiff < 3600)
                {
                    return string.Format(GetDate("MINUTES_AGO"),
                        Math.Floor((double)_secDiff / 60));
                }
                // D.
                // Less than 2 hours ago.
                if (_secDiff < 7200)
                {
                    return string.Format(GetDate("HOURS_AGO"), 1); ;
                }
                // E.
                // Less than one day ago.
                if (_secDiff < 86400)
                {
                    return string.Format(GetDate("HOURS_AGO"),
                        Math.Floor((double)_secDiff / 3600));
                }
            }
            // 6.
            // Handle previous days.
            if (_dayDiff == 1)
            {
                var _dt = _date + TimeZoneOffsetUser;
                return string.Format(GetDate("YESTERDAY_AT"), _dt);
            }
            if (_dayDiff < 7)
            {
                return string.Format(GetDate("DAYS_AGO"), _dayDiff);
            }
            return _dayDiff < 31 ? string.Format(GetDate("WEEKS_AGO"), Math.Ceiling((double)_dayDiff / 7)) : DateTimeFormat(_date);

            // Default full date format
        }



        #endregion


        #region Validate

        public static bool NameIsDisallowed(this string name)
        {


            if (!String.IsNullOrEmpty(Configuration.DisallowedUserNames))
            {
                const string regExWildcard = @"[\w\W]*?"; //"[a-zA-Z0-9]*";
                const RegexOptions regExOpt = RegexOptions.IgnoreCase;
                string[] _names = Configuration.DisallowedUserNames.Split();

                return _names.Select(s => String.Format(@"^{0}$", s.Replace("*", regExWildcard))).Select(thePattern => new Regex(thePattern, regExOpt)).Any(regEx => regEx.IsMatch(name));
            }
            return false;
        }


        public static bool EmailIsValid(this string email)
        {

            if (string.IsNullOrEmpty(Configuration.AllowEmails) || "*".Equals(Configuration.AllowEmails))
            {
                return true;
            }
            else
            {
                const string regExWildcard = @"[_a-z0-9-]+(\.[_a-z0-9-]+)*";//@"[^\w\-\.]";//"[A-Z0-9._%+-]+";
                const RegexOptions regExOpt = RegexOptions.IgnoreCase;
                string[] _Names = Configuration.AllowEmails.Split(new char[] { ',', ';' });

                return _Names.Select(s => String.Format(@"^{0}$", s.Replace("*", regExWildcard))).Select(thePattern => new Regex(thePattern, regExOpt)).Any(regEx => regEx.IsMatch(email));

            }
        }

        #endregion


        #region URL Format

        public static string GetAbsoluteUrl(string _pageStringFormat, params object[] _params)
        {

            if (_pageStringFormat == ForumUrls.HOME_PAGE || _pageStringFormat == ForumUrls.DEFAULT)
                return Configuration.AbsoluteUrl;


            int _length = _params.Length + 2;
            var _args = new string[_length];
            _args[0] = Configuration.AbsoluteUrl;// first arg set Root Path

            for (int i = 0; i < _params.Length; i++)
            {
                _args[i + 1] = _params[i].ToString();
            }
            _args[_length - 1] = Configuration.Extension; // Last arg set extension


            return string.Format(_pageStringFormat, _args);
        }

        public static string ToPlainText(this string title)
        {
            if (Configuration.Language.Equals("vn", StringComparison.OrdinalIgnoreCase))
                title = title.VietNamToPlainText();
            return title.ToFriendlyUrl();
        }

        /* public static string GetAbsoluteUrl(string _pageStringFormat)
         {
             return GetAbsoluteUrl(_pageStringFormat) string.Format(_pageStringFormat, Configuration.AbsoluteUrl, Configuration.Extension);
         }*/

        public static string ForumLink(int _id, string _title, object _pageIndex)
        {

            if (_pageIndex != null)
            {
                return string.Format(ForumUrls.FORUM_URL_PAGE, Configuration.AbsoluteUrl, _id, _pageIndex, _title.ToPlainText(), Configuration.Extension);

            }
            return string.Format(ForumUrls.FORUM_URL, Configuration.AbsoluteUrl, _id, _title.ToPlainText(), Configuration.Extension);

        }

        public static string Feed(int _id, string _title)
        {

            return GetAbsoluteUrl(ForumUrls.RSS_FEED, _id, _title.ToPlainText());
        }

        public static string ForumLink(int _id, string _title)
        {
            return ForumLink(_id, _title, null);
        }

        public static string ThreadLink(int _id, string _title)
        {
            return ThreadLink(_id, _title, null);
        }

        public static string ThreadLink(int _id, string _title, object _pageIndex)
        {

            var _isNumber = _pageIndex.IsNumeric();

            string _link = ((_isNumber && Convert.ToInt32(_pageIndex) > 1) || (!_isNumber && _pageIndex != null))
                                ? GetAbsoluteUrl(ForumUrls.THREAD_URL_PAGE, _id, _pageIndex, _title.ToPlainText())
                               : GetAbsoluteUrl(ForumUrls.THREAD_URL, _id, _title.ToPlainText());


            if (!string.IsNullOrEmpty(Context.Request.QueryString["highlight"]))
            {
                _link += "?highlight=" + Context.Server.UrlEncode(Context.Request.QueryString["highlight"]);

            }

            return _link;

        }

        /* public static string ThreadLink(int _id, string _title, int _pageIndex)
         {

            /* int _p;

             int.TryParse(_pageIndex.ToString(), out _p);#1#


             string _link = _pageIndex > 1
                                ? GetAbsoluteUrl(ForumUrls.THREAD_URL_PAGE, _id, _title.ToPlainText())
                                : GetAbsoluteUrl(ForumUrls.THREAD_URL, _id, _title.ToPlainText());// (_pageIndex != null) ? string.Format(ForumUrls.THREAD_URL_PAGE, Configuration.AbsoluteUrl, _id, _pageIndex, _title.ToPlainText(), Configuration.Extension) : string.Format(ForumUrls.THREAD_URL, Configuration.AbsoluteUrl, _id, _title.ToPlainText(), Configuration.Extension);


           

             return _link;
         }
 */

        public static string FindPost(int _id, string _title)
        {
            return string.Format(ForumUrls.FIND_POST_X, Configuration.AbsoluteUrl, _id, _title.ToPlainText(), Configuration.Extension);
        }


        #endregion

        #region Encrypt & Decrypt

        public static string Encrypt(params object[] _params)
        {

            var _sb = new StringBuilder();

            for (int i = 0; i < _params.Length; i++)
            {

                if (i > 0)
                    _sb.Append(",");
                _sb.Append(_params[i].ToString());
            }

            // More
            _sb.AppendFormat(",{0}", SessionID);

            return Encryptor.Encrypt(_sb.ToString(), Configuration.EncryptPassword);

        }


        public static string[] Decrypt(string _encryptString)
        {

            string _plainText = Encryptor.Decrypt(_encryptString, Configuration.EncryptPassword);
            return _plainText.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
        }




        #endregion

        #region Utils

        public static IHtmlString IconRender(Topic _topic, int forumId)
        {
            string _type = string.Empty;


            if (_topic.Priority > 0)
            {

                _type = (_topic.Priority == (int)PriorityType.Notice) ? "notice" : "sticky";


            }
            else
            {
                if (_topic.IsLocked)
                {
                    _type = "locked";

                }

                else if (!string.IsNullOrEmpty(_topic.RemoteUrl))
                {
                    _type = "link";
                }
                else if (_topic.IsPoll)
                {
                    _type = "poll";
                }

                else if (_topic.XForumId.HasValue && _topic.XForumId.Value > 0 && _topic.XForumId.Value == forumId)
                {
                    _type = "moved";
                    goto Finish;
                }
                else if (_topic.Replies > Configuration.HotReplyThreshold && _topic.LastPosted > (DateTime.UtcNow - TimeSpan.FromDays(Configuration.NewReplyDaysAgo)))
                {
                    _type = "hot";
                }

                var _new = (_topic.LastPostId > 0 &&
                            _topic.LastPosted > (DateTime.UtcNow - TimeSpan.FromDays(Configuration.NewReplyDaysAgo)) ||
                            _topic.Created > (DateTime.UtcNow - TimeSpan.FromDays(Configuration.NewReplyDaysAgo)));



                _type = string.Format("{0}{1}", _type, _new ? (!string.IsNullOrWhiteSpace(_type) ? "-" : "") + "new" : " not-new");

            }

        Finish:

            return MvcHtmlString.Create(string.Format("<span class=\"t-icon {0}\">&nbsp;</span>", _type));
        }

        public static IHtmlString IconRender(Forum _forum)
        {

            string _type = "not-new";
            string _style = "";

            if (!string.IsNullOrWhiteSpace(_forum.Icon))
            {
                _style = string.Format("background:url('{0}') no-repeat 0 0", _forum.Icon);
            }

            if (_forum.IsLocked)
                _type = "locked";
            else if (!string.IsNullOrEmpty(_forum.RemoteUrl))
                _type = "link";
            else if (_forum.LastPostId > 0 && _forum.LastPostDate > (DateTime.UtcNow - TimeSpan.FromDays(Configuration.NewReplyDaysAgo)))

                _type = "new";


            return MvcHtmlString.Create(string.Format("<span class=\"f-icon {0}\" style=\"{1}\">&nbsp;</span>", _type, _style));
        }



        public static bool Compare(ForumUrl a, ForumUrl b)
        {
            var _val = a != null ? !string.IsNullOrWhiteSpace(a.ParentUrl) ? a.ParentUrl : !string.IsNullOrWhiteSpace(a.ShortUrl) ? a.ShortUrl : a.Url : string.Empty;
            return b.Url.Compare(_val);
        }


        public static String Padding(int i)
        {

            if (i <= 1) return string.Empty;
            string _out = "|";

            for (int j = 0; j < i; j++)
            {
                _out += "-";
            }
            return _out;
        }

        public static string RemoveLineBreaks(this string lines)
        {
            return lines.Replace("\r", "").Replace("\n", " ").Replace("\t", "");
        }


        /* public static string TruncateAtWord(this string input, int length)
         {
             if (string.IsNullOrWhiteSpace(input))
                 return string.Empty;
             input = input.Replace("&nbsp;", " ").RemoveLineBreaks();
             int _iNextSpace = input.LastIndexOf(" ", length);
             return string.Format("{0} ...", input.Substring(0, (_iNextSpace > 0) ? _iNextSpace : length).Trim());
         }*/

        public static string TruncateAtWord(this string value, int length)
        {

            return value.Truncate(length, true, true);
        }





        public static string Truncate(this string input)
        {

            return string.IsNullOrWhiteSpace(input) ? "" : input.TruncateAtWord(Configuration.TopicShowWordsLength);

        }


        public static string Truncate(this string s, int length, bool atWord, bool addEllipsis)
        {

            //http://nickstips.wordpress.com/2010/02/12/c-truncate-a-string-at-the-end-of-a-word/

            // Return if the string is less than or equal to the truncation length
            if (s == null || s.Length <= length)
                return s;

            // Do a simple tuncation at the desired length
            string s2 = s.Replace("&nbsp;", " ").Substring(0, length);

            // Truncate the string at the word
            if (atWord)
            {
                // List of characters that denote the start or a new word (add to or remove more as necessary)
                var alternativeCutOffs = new List<char> { ' ', ',', '.', '?', '/', ':', ';', '\'', '\"', '\'', '-', '+', '|' };

                // Get the index of the last space in the truncated string
                int lastSpace = s2.LastIndexOf(' ');

                // If the last space index isn't -1 and also the next character in the original
                // string isn't contained in the alternativeCutOffs List (which means the previous
                // truncation actually truncated at the end of a word),then shorten string to the last space
                if (lastSpace != -1 && (s.Length >= length + 1 && !alternativeCutOffs.Contains(s.ToCharArray()[length])))
                    s2 = s2.Remove(lastSpace);
            }

            // Add Ellipsis if desired
            if (addEllipsis)
                s2 += "...";

            return s2;
        }

        /// <summary>
        /// Finds web and email addresses in a string and surrounds then with the appropriate HTML anchor tags 
        /// </summary>
        /// <param name="s"></param>
        /// <returns>String</returns>
        public static string ParseLinks(this string s)
        {
            //"@\b(https?://)?(([0-9a-zA-Z_!~*'().&=+$%-]+:)?[0-9a-zA-Z_!~*'().&=+$%-]+\@)?(([0-9]{1,3}\.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+\.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z]\.[a-zA-Z]{2,6})(:[0-9]{1,4})?((/[0-9a-zA-Z_!~*'().;?:\@&=+$,%#-]+)*/?)@";

            /*//Finds URLs with no protocol
            var _urlregex = new Regex(@"\b\({0,1}(?<url>(www|ftp)\.[^ ,""\s<)]*)\b",
              RegexOptions.IgnoreCase | RegexOptions.Compiled);
            //Finds URLs with a protocol
            var _httpurlregex = new Regex(@"\b\({0,1}(?<url>[^>](http://www\.|http://|https://|ftp://)[^,""\s<)]*)\b",
              RegexOptions.IgnoreCase | RegexOptions.Compiled);
            //Finds email addresses
            var _emailregex = new Regex(@"\b(?<mail>[a-zA-Z_0-9.-]+\@[a-zA-Z_0-9.-]+\.\w+)\b",
              RegexOptions.IgnoreCase | RegexOptions.Compiled);
            s = _urlregex.Replace(s, " <a href=\"http://${url}\">${url}</a>");
            s = _httpurlregex.Replace(s, " <a href=\"${url}\">${url}</a>");
            s = _emailregex.Replace(s, "<a href=\"mailto:${mail}\">${mail}</a>");
            return s;
*/

            var url_regex = new Regex(@"(http:\/\/([\w.]+\/?)\S*)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            var email_regex = new Regex(@"([a-zA-Z_0-9.-]+\@[a-zA-Z_0-9.-]+\.\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            s = url_regex.Replace(s, "<a href=\"$1\" target=\"_blank\">$1</a>");
            s = email_regex.Replace(s, "<a href=mailto:$1>$1</a>");

            return s;

        }

        public static string HtmlToPlainText(this string html)
        {
            var reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase);
            return reg.Replace(html, string.Empty);
        }


        public static string HtmlSanitize(this string html)
        {
            return HtmlSanitizer.Sanitize(html);//Sanitizer.GetSafeHtmlFragment(html);
        }


        public static string BadWordClean(this string html)
        {
            return html;
        }

        public static IHtmlString HtmlClean(this string html)
        {
            return html.HtmlClean(false, 0);
        }

        public static IHtmlString HtmlClean(this string html, bool liked, int posts)
        {

            return MvcHtmlString.Create(html.BbCodeToHtml(liked, posts).HighlightKeywords().BadWordClean().ParseOutLinks());

        }


        public static string ReturnUrl
        {
            get
            {
                return !string.IsNullOrWhiteSpace(Context.Request.QueryString["ReturnUrl"])
                                  ? Context.Request.QueryString["ReturnUrl"]
                                  : Context.Request.UrlReferrer != null ? Context.Request.UrlReferrer.ToString() : "/";
            }
        }


        private static bool IsDoFollowDomain(string domain)
        {

            if (!string.IsNullOrWhiteSpace(Configuration.AllowFollowDomains))
            {

                string[] _domains = Configuration.AllowFollowDomains.Split(new char[] { ',', ';' },
                                                                           StringSplitOptions.RemoveEmptyEntries);

                return _domains.Any(x => x.ToLower() == domain);

            }
            return true;

        }


        public static string ParseOutLinks(this string html)
        {

            if (Configuration.NoFollowExternalLinks)
            {


                var _doc = new HtmlDocument { OptionWriteEmptyNodes = true };
                _doc.LoadHtml(html);

                IList<HtmlNode> _hnc = _doc.DocumentNode.Descendants("a").ToList();

                var _domain = Context.Request.Url;

                foreach (var node in _hnc)
                {
                    try
                    {
                        var _a = node.Attributes["href"].Value;

                        var _href = !string.IsNullOrWhiteSpace(_a) ? new UriBuilder(_a) : null;

                        //UriBuilder
                        if (_href != null && !IsDoFollowDomain(_href.Host.ToLower()) && _href.Host.ToLower() != _domain.Host.ToLower())
                        {
                            node.Attributes.Add("rel", "nofollow");
                        }

                    }
                    catch (Exception)
                    {


                    }



                }

                return _doc.DocumentNode.WriteTo();

            }


            return html;
        }





        #endregion





        #region BBCodeToHtml


        public static string BbCodeToHtml(this string html, bool liked, int posts)
        {

            // return BBCodeHelper.Format(html);


            /*html = html.Replace("\r", "");
            html = html.Replace("\n\n", "</p><p>");
            html = html.Replace("\n", "<br />");*/


            #region Quote

            string quoteStart = string.Format(
                       "<div class=\"quote-wrapper\"><div class=\"quote-post-by\">{0}<strong>$1</strong><a href=\"{1}\" title=\"{2}\" class=\"quote-findpost\"></a></div><div class=\"quote-content\">",
                       GetText(LanguageSection.TOPICS, "original_post_by"), GetAbsoluteUrl(ForumUrls.FIND_POST, "$2"), GetText(LanguageSection.TOPICS, "view_original_post"));

            const string quoteEmptyStart = "<div class=\"quote-wrapper\"><div class=\"quote-content\">";


            //@"\[quote=((.|\n)*?);((.|\n)*?)(?:\s*)\])"
            html = new RegexFormatter(@"\[QUOTE=(.*);(.*)\]", quoteStart).Format(html);

            html = new RegexFormatter(@"\[QUOTE(?:\s*)\]", quoteEmptyStart).Format(html);

            html = new RegexFormatter(@"\[/QUOTE\]", "</div></div>").Format(html);






            #endregion




            #region Code

            html = new RegexFormatter(@"\[code=(.*?)\]", "<code name=\"code\" class=\"brush: {1}\">").Format(html);
            html = new RegexFormatter(@"\[/code\]", "</code>").Format(html);

            #endregion


            #region Hidden

            html = new RegexFormatter(@"\[hide](.*)\[/hide\]", Logined ? "<div class=\"hidden-wrapper\">$1</div>" : string.Format("<div class=\"hidden-wrapper\">{0}</div>", GetText(LanguageSection.TOPICS, "view_content_login_required"))).Format(html);

            #endregion

            /*

            _regex = new Regex(@"\[code=(.*?)\](.*?)\[/code\]", _options);

            while (_regex.IsMatch(html))
                html = _regex.Replace(html, "<code name=\"code\" class=\"brush: {1}\">$2</code>");



            _regex = new Regex(@"\[hide](.*)\[/hide\]", _options);

            while (_regex.IsMatch(html))
            {
                if (Logined)
                {
                    html = _regex.Replace(html, "<div class=\"hidden-wrapper\">$1</div>");
                }
                else
                {
                    html = _regex.Replace(html, string.Format("<div class=\"hidden-wrapper\">{0}</div>", GetText(LanguageSection.TOPICS, "view_content_login_required")));
                }
            }


            // YouTube

            //_regex = new Regex(@"http:\/\/(?:www\.)?youtube.com\/watch\?(?=.*v=[a-zA-Z0-9_\-]+)(?:\S+)?$");//new Regex(@"\[youtube\]http:\/\/([a-zA-Z]+.)youtube.com\/watch\?v=([a-zA-Z0-9_\-]+)\[\/youtube\]", _options);


            _regex = new Regex(@"\[youtube\](.*?)\[\/youtube\]", _options);


            if (_regex.IsMatch(html))
            {
                html = html.Replace(html,
                               @"<object width=""425"" height=""344""><param name=""movie"" value=""http://www.youtube.com/v/$1""></param><param name=""allowFullScreen"" value=""true""></param><embed src=""http://www.youtube.com/v/$1"" type=""application/x-shockwave-flash"" allowfullscreen=""true"" width=""425"" height=""344""></embed></object>");
            }
*/

            return html;

        }

        #endregion


        #region HighLight Keywords

        public static string HighlightKeywords(this string text, string keywords, bool fullMatch)
        {
            if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(keywords))
                return text;
            var words = keywords.Split(new[] { ',', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
            if (!fullMatch)
                return words.Select(word => word.Trim()).Aggregate(text,
                             (current, pattern) =>
                             Regex.Replace(current,
                                             pattern,
                                               "<span class=\"highlight\">$0</span>",
                                               RegexOptions.IgnoreCase));

            return words.Select(word => "\\b" + word.Trim() + "\\b").Aggregate(text, (current, pattern) => Regex.Replace(current, pattern, "<span class=\"highlight\">$0</span>", RegexOptions.IgnoreCase));

        }



        public static string HighlightKeywords(this string input, string keywords)
        {

            return input.HighlightKeywords(keywords, true);

            /*if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(keywords))
            {
                return input;
            }

            string[] _kws = keywords.Split(' ');
            return _kws.Aggregate(input, (current, _k) => Regex.Replace(current, _k, string.Format("<span class=\"highlight\">{0}</span>", "$0"), RegexOptions.IgnoreCase));*/
        }


        public static string HighlightKeywords(this string input)
        {

            string _hl = Context.Request.QueryString["highlight"];

            return input.HighlightKeywords(_hl);

        }

        #endregion


        #region PageRender

        public static string PageRender(string pageFormatString, int pageIndex, int pageSize, int totalRows)
        {

            var totalPage = (int)Math.Ceiling((double)totalRows / pageSize);

            if (totalPage <= 1) return string.Empty;

            const int pageButtonCount = 3;
            int min = pageIndex - pageButtonCount;
            int max = pageIndex + pageButtonCount;

            if (max > totalPage)
                min -= max - totalPage;
            else if (min < 1)
                max += 1 - min;

            var _sb = new StringBuilder(1000);
            bool needDiv = false;


            _sb.Append("<ul class=\"paging\">");


            _sb.Append("<li class=\"page-x-of-y\"><span>");
            _sb.AppendFormat(GetText(LanguageSection.GLOBAL, "PAGE_X_OF_Y"), pageIndex, totalPage);
            _sb.Append("</span></li>");

            //Ren Prev Button
            if (pageIndex > 1)
            {
                _sb.AppendFormat(
               "<li class=\"{1}\"><a title=\"Go to First Page\" href=\"{0}\"></a></li>", string.Format(pageFormatString, 1), "page-first");


                _sb.AppendFormat("<li class=\"page-prev\"><a href=\"{0}\" title=\"Go to Previous Page\">&nbsp;&nbsp;</a></li>", string.Format(pageFormatString, pageIndex - 1));


            }



            for (int i = 1; i <= totalPage; i++)
            {
                if (i <= 2 || i > totalPage - 2 || (min <= i && i <= max))
                {

                    string _className = (i == pageIndex) ? "actived" : "page-item";

                    _sb.AppendFormat("<li class=\"{1}\"><a href=\"{0}\">{2}</a></li>", string.Format(pageFormatString, i), _className, i);
                    needDiv = true;
                }
                else if (needDiv)
                {
                    _sb.AppendFormat("<li class=\"{0}\"><a>...</a></li>", "page-item");
                    needDiv = false;
                }
            }

            //Ren Next Button
            if (pageIndex < totalPage)
            {
                _sb.AppendFormat(
                    "<li class=\"{1}\"><a title=\"Go to Next Page\" href=\"{0}\"></a></li>", string.Format(pageFormatString, pageIndex + 1), "page-next");

                _sb.AppendFormat(
                  "<li class=\"{1}\"><a title=\"Go to Last Page\" href=\"{0}\"></a></li>", string.Format(pageFormatString, totalPage), "page-last");


            }


            _sb.Append("</ul>");

            return _sb.ToString();


        }


        public static string ReadHtml(string filename)
        {
            return Utils.ReadFileToString(Context.Server.MapPath(string.Format("{0}Html/{1}.htm", ApplicationPath, filename)));
        }


        #endregion


        #region Send Mail


        public static void SendMail(string from, string to, string subject, string body)
        {


            try
            {

                var _mail = new MailMessage(new MailAddress(from, Configuration.ForumName), new MailAddress(to))
                {
                    Subject = string.Format("[{0}] - {1}", Configuration.ForumName, subject),
                    Body = body + GetText(LanguageSection.SEND_EMAIL, "SIGNATURE"),
                    BodyEncoding = Encoding.UTF8,
                    IsBodyHtml = true,
                    Priority = MailPriority.High
                };


                //SmtpClient smtpMail = new SmtpClient("smtp.gmail.com");
                var _smtpMail = new SmtpClient(Configuration.SmtpServer, Configuration.SmtpPort)
                {
                    UseDefaultCredentials = false,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    EnableSsl = Configuration.SmtpSsl
                };

                if (Configuration.SmtpCredentials)
                {
                    _smtpMail.Credentials = new NetworkCredential(Configuration.SmtpUserName, Configuration.SmtpUserPass);
                }

                // and then send the mail
                _smtpMail.Send(_mail);
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }




        }


        #endregion



    }
}
